package org.jetlinks.community.mstn.logging;

import lombok.Getter;
import lombok.Setter;
import org.hswebframework.ezorm.core.param.Term;
import org.hswebframework.web.logging.RequestInfo;
import reactor.core.publisher.Mono;

import java.io.PrintWriter;
import java.io.Serializable;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.StringJoiner;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;

@Getter
@Setter
public class MstnAssetsRequestCtx {

    private String id;
    private String tenantId;
    private Method method;
    private Class<?> target;
    private Map<String, Object> parameters;
    private String ip;
    private String url;
    private Map<String, String> httpHeaders;
    private Map<String, String> context;
    private String httpMethod;
    private Object response;
    private long requestTime;
    private long responseTime;
    private Throwable exception;
    private Term term;

    public MstnAssetsRequestCtx() {
    }

    public Map<String, Object> toSimpleMap(Function<Object, Serializable> objectFilter, Map<String, Object> map) {
        if (this.method != null) {
            StringJoiner methodAppender = new StringJoiner(",", this.method.getName().concat("("), ")");
            String[] parameterNames = (String[])this.parameters.keySet().toArray(new String[0]);
            Class<?>[] parameterTypes = this.method.getParameterTypes();

            for(int i = 0; i < parameterTypes.length; ++i) {
                methodAppender.add(parameterTypes[i].getSimpleName().concat(" ").concat(parameterNames.length > i ? parameterNames[i] : "arg" + i));
            }

            map.put("method", methodAppender.toString());
        }

        map.put("target", this.target != null ? this.target.getName() : "");
        Map<String, Object> newParameter = new LinkedHashMap(this.parameters);
        newParameter.entrySet().forEach((entry) -> {
            if (entry.getValue() != null) {
                entry.setValue(objectFilter.apply(entry.getValue()));
            }

        });
        map.put("parameters", newParameter);
        map.put("httpHeaders", this.httpHeaders);
        map.put("httpMethod", this.httpMethod);
        map.put("ip", this.ip);
        map.put("url", this.url);
        map.put("response", objectFilter.apply(this.response));
        map.put("requestTime", this.requestTime);
        map.put("responseTime", this.responseTime);
        map.put("id", this.id);
        map.put("useTime", this.responseTime - this.requestTime);
        if (this.exception != null) {
            StringWriter writer = new StringWriter();
            this.exception.printStackTrace(new PrintWriter(writer));
            map.put("exception", writer.toString());
        }

        return map;
    }

    public void putCtxInfo(MstnAssetsRequestInfo info) {
        this.setIp(info.getIpAddr());
        this.setHttpMethod(info.getRequestMethod());
        this.setHttpHeaders(info.getHeaders());
        this.setUrl(info.getPath());
        this.setContext(info.getContext());
    }

    public void putContext(Map<String, String> context) {
        if (this.context == null) {
            this.context = new ConcurrentHashMap();
        }

        this.context.putAll(context);
    }

    public void putContext(String key, Object value) {
        if (this.context == null) {
            this.context = new ConcurrentHashMap();
        }

        this.context.put(key, String.valueOf(value));
    }
}
